package com.googlecode.afx.view.fxml;

import java.util.Collection;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.CollectionUtils;

import javafx.collections.ObservableList;
import javafx.scene.Node;

import com.googlecode.afx.view.NodeWrapper;
import com.googlecode.afx.view.binding.BeanWrapperAdapter;

/**
 * Simple holder object for the model instance, derived from <tt>BeanWrapperAdapter</tt> in order
 * to make use of binding.
 * 
 * @author MartinKoster
 *
 */
public class ModelHolder extends BeanWrapperAdapter {

	private static final Log LOG = LogFactory.getLog(ModelHolder.class);
	
	private Object model;
	
	public ModelHolder(Object model) {
		super(model);
		this.model = model;
	}

	/**
	 * Refreshes bindings. This methods should be called after bean properties have been changed 
	 * and these changes shall be immediately reflected in the UI.
	 * 
	 */
	@SuppressWarnings("unchecked")
	public void refreshBindings() {
		this.setBean(this.model);
	}

	public Object getModel() {
		return model;
	}


	public void setModel(Object model) {
		this.model = model;
	} 
	
	public void bind(String path, Node node) {
		LOG.debug("Binding object of type '" + this.model.getClass().getName() + "': Property path '" + path + "' to node '" + node.getClass().getName() + "'.");
		this.bind(path, null, node, null);
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public void bind(String path, Class<?> elementType, Node node, String propertyName) {
		LOG.debug("Binding object of type '" + this.model.getClass().getName() + "': Property path '" + path + "' to node '" + node.getClass().getName() + "', using property method '" + path + "'.");

		NodeWrapper nodeWrapper = new NodeWrapper(node);

		String propertyToAccess = propertyName != null ? propertyName : nodeWrapper.getPrimaryValueProperty();
		
		Class type = this.getPropertyType(path);

		if(Collection.class.isAssignableFrom(type)) {

			Class contentType = null;
			if(elementType != null && !Object.class.equals(elementType)) {
				contentType = elementType;
			} else {
				contentType = CollectionUtils.findCommonElementType((Collection<?>) this.getPropertyValue(path));
			}
			
			ObservableList items = nodeWrapper.getObservableList(propertyToAccess);
			this.bindContentBidirectional(path, null, null,
					items, contentType, null, null);
			
		} else {
			this.bindBidirectional(path, nodeWrapper.getProperty(propertyToAccess), type);
		}

	}
}
